//
//  GameState.m
//  puzzle
//
//  Created by omega on 09/05/05.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "GameState.h"
#import "hellApp.h"

#import <OpenGLES/ES1/gl.h>
#import "string.h"

#import "math.h"
#import "Effect.h"
#import "GameStateGraphics.h"

PlayState play;
	
int chain_counter;
int continuing_chain_counter;
int continuing_counter;

int next_line_drop_x;
float slide_dist;
bool grab;


//---------
bool check_chain_erase_panel();
bool check_panel_will_erase();
bool check_erase_panel();
bool check_fall_panel();
void erase_ignited_panel();
int count_erasing_now();
int get_field_count();
int get_field_height();
int get_field_lowest();
//---------

inline Panel* get_field_panel(int x,int y){
	return &(play.panel[x][y]);
}

void set_next_line(){
	int t,w,d;
	w = (rand() / 43) % 2+2;
	for(d = 0 ; d < PANEL_NEXT_QUEUE_LEN-1 ; d++ ){
		for(t = 0 ; t < PANEL_FIELD_W ; t++ ){
			play.next_line[t][d] = play.next_line[t][d+1];
		}
	}
	
	
	for(t = 0 ; t < PANEL_FIELD_W ; t++ ){
		if( t < w ){
			play.next_line[t][PANEL_NEXT_QUEUE_LEN - 1] = (rand() / 37) % (PANEL_TYPE_MAX-1) + 1;
		}else{
			play.next_line[t][PANEL_NEXT_QUEUE_LEN - 1] = PANEL_TYPE_NONE;
		}
	}
	next_line_drop_x = (PANEL_FIELD_W - w) / 2;
}

int get_next_line_width(int d){
	int t;
	for(t = 0 ; t < PANEL_FIELD_W ; t++){
		if( play.next_line[t][d] == PANEL_TYPE_NONE ) return t;
	}
	return PANEL_FIELD_W;
}

void init_field_line( int y , Panel_Type ty ){
	int x;
	for(x = 0 ; x < PANEL_FIELD_W ; x++ ){
		bool is_retry = FALSE;
		
		do{
			if( ty == PANEL_TYPE_RAND ){
				play.panel[x][y].type = (rand() / 37) % (PANEL_TYPE_MAX - PANEL_TYPE_RED) + PANEL_TYPE_RED;
			}else{
				play.panel[x][y].type = ty;
			}
			play.panel[x][y].timer = 0;
			play.panel[x][y].state = PANEL_STATE_STAYING;
			play.panel[x][y].fall_diff = 0;
			play.panel[x][y].is_player_droped = FALSE;
			
			// check block pattern( avoid appear with erasing )
			if( y > 0 && x > 0 && ty != PANEL_TYPE_NONE ){
				if( play.panel[x][y].type == play.panel[x][y].type && play.panel[x][y].type == play.panel[x-1][y].type 
				   && play.panel[x][y].type == play.panel[x][y-1].type && play.panel[x][y].type == play.panel[x-1][y-1].type )
				{
					is_retry = TRUE;
				}else{
					is_retry = FALSE;
				}
			}
		}while(is_retry);
	}
}

void raise_up_field( float raise ){
	play.raised_field += raise;
	if( play.raised_field >= 1.0f ){
		play.raised_field = 0.0f;
		int x,y;
		for( x = 0 ; x < PANEL_FIELD_W ; x++ ){
			for( y = 0 ; y < PANEL_FIELD_H-1 ; y++ ){
				memcpy( &play.panel[x][y] , &play.panel[x][y+1] , sizeof(Panel) );
			}
		}
		
		play.scrolled_dist++;
		init_field_line( PANEL_FIELD_H - 1 , PANEL_TYPE_RAND );
	}
}

void init_field(){
	int y;
	for(y = 0 ; y < PANEL_FIELD_H ; y++){
		init_field_line( y , PANEL_TYPE_NONE );
	}
	for(y = 0 ; y < PANEL_NEXT_QUEUE_LEN ; y++){
		play.next_line[0][y] = PANEL_TYPE_NONE;
	}
	
	play.player_timer = 0;
	play.raised_field = 0;
	play.score = 0;
	play.scrolled_dist = 0;
	play.cleared_panel_counter = 0;
	
	chain_counter = 0;
	continuing_chain_counter = 0;
	grab = FALSE;
	
	while( play.next_line[0][0] == PANEL_TYPE_NONE ) set_next_line();
}


void GameState_initLoad(){
	init_field();
	Hell_loadBgm( "bgm1.mp3" );
	Hell_playBgm();
}


void GameState_update(){

	play.player_timer++;
	
	bool is_falling = check_fall_panel();
	bool is_erasing = check_chain_erase_panel();
	bool is_will_erasing = check_panel_will_erase();
	if(is_will_erasing == TRUE){
		Hell_playSoundEffect( HELL_SOUND_FIRE );
	}
//	if( is_falling == FALSE  && is_erasing == FALSE ){
		check_erase_panel();
//	}
	
	if( is_falling == FALSE && is_erasing == FALSE && play.erasing_timer == 0 ){
		for(int x = 0 ; x < PANEL_FIELD_W ; x++ ){
			for(int y = 0 ; y < PANEL_FIELD_H-2; y++ ){
				if( play.panel[x][y].type == PANEL_TYPE_NONE || play.panel[x][y].state != PANEL_STATE_STAYING ) continue;
				
				int slide_x = (x < PANEL_FIELD_W/2) ? (1) : (-1);
				if( x + slide_x >= 0 && x + slide_x < PANEL_FIELD_W
				   && play.panel[x + slide_x][y].type == PANEL_TYPE_NONE && play.panel[x + slide_x][y+1].type == PANEL_TYPE_NONE && play.panel[x + slide_x][y+2].type == PANEL_TYPE_NONE){
					memcpy( &play.panel[x + slide_x][y] , &play.panel[x][y] , sizeof(Panel) );
					play.panel[x][y].type = PANEL_TYPE_NONE;
					play.panel[x + slide_x][y].state = PANEL_STATE_FALLING;
				}else if(x - slide_x >= 0 && x - slide_x < PANEL_FIELD_W
					&& play.panel[x - slide_x][y].type == PANEL_TYPE_NONE && play.panel[x - slide_x][y+1].type == PANEL_TYPE_NONE && play.panel[x - slide_x][y+2].type == PANEL_TYPE_NONE){
					memcpy( &play.panel[x - slide_x][y] , &play.panel[x][y] , sizeof(Panel) );
					play.panel[x][y].type = PANEL_TYPE_NONE;
					play.panel[x - slide_x][y].state = PANEL_STATE_FALLING;
				}	
				break;
			}
		}
	}
	
	if( get_field_count() < PANEL_RAISE_BORDER && get_field_height() > 0 ){
		raise_up_field( PANEL_RAISE_SPEED );
	}
	
	chain_counter = count_erasing_now();
	if( is_erasing ){
		play.erasing_timer++;
		if( play.erasing_timer > PANEL_ERASE_TIME_CLEAR ){
			play.erasing_timer = 0;
			erase_ignited_panel();
			continuing_counter++;
			continuing_chain_counter += chain_counter;
			chain_counter = 0;
		}
	}
	
	static int continuing_timer;
	if( !is_erasing && !is_falling ){
		if( continuing_timer++ > 5 ){
			continuing_chain_counter = 0;
			continuing_counter = 0;
		}
	}else{
		continuing_timer = 0;
	}
	
	GameState_updateEffect();
}

void add_effect_pertic_with_field( int field_x , int field_y , int cnt ){
	for(int t=0;t<cnt;t++)
		GameState_addEffect(field_x * PANEL_SIZE + PANEL_FIELD_OFFSET_X , (PANEL_FIELD_H - field_y + play.raised_field) * PANEL_SIZE + PANEL_FIELD_OFFSET_Y,
				   (rand() % 127 - 63)*0.1 , (rand() % 127 - 31) * 0.1 , EFFECT_TYPE_CROSS , DONTCARE_INT );
}

bool check_chain_erase_panel(){
	int x,y;
	bool is_erasing = FALSE;
	
	for(y = 0 ; y < PANEL_FIELD_H ; y++ ){
		for(x = 0 ; x < PANEL_FIELD_W ; x++ ){
			if( play.panel[x][y].type == PANEL_TYPE_NONE ) continue;
			
			switch( play.panel[x][y].state ){
				case PANEL_STATE_STAYING:
					// 延焼
					if(( x > 0 && play.panel[x-1][y].state == PANEL_STATE_ERASING && play.panel[x-1][y].type == play.panel[x][y].type && play.panel[x-1][y].timer > PANEL_ERASE_TIME_AFFECT)
					   ||( y > 0 && play.panel[x][y-1].state == PANEL_STATE_ERASING && play.panel[x][y-1].type == play.panel[x][y].type && play.panel[x][y-1].timer > PANEL_ERASE_TIME_AFFECT)
					   ||( x < PANEL_FIELD_W-1 && play.panel[x+1][y].state == PANEL_STATE_ERASING && play.panel[x+1][y].type == play.panel[x][y].type && play.panel[x+1][y].timer > PANEL_ERASE_TIME_AFFECT)
					   ||( y < PANEL_FIELD_H-1 && play.panel[x][y+1].state == PANEL_STATE_ERASING && play.panel[x][y+1].type == play.panel[x][y].type && play.panel[x][y+1].timer > PANEL_ERASE_TIME_AFFECT ))
					{
						play.panel[x][y].state = PANEL_STATE_ERASING;
						play.panel[x][y].timer = 0;
						play.erasing_timer = 0;
						
						add_effect_pertic_with_field( x , y , 4 );
					}					
					break;
				case PANEL_STATE_ERASING:
					play.panel[x][y].timer++;
					is_erasing = TRUE;
					break;
				default:
					break;
			}
		}
	}
	
	return is_erasing;
}

bool check_fall_panel(){
	int x,y;
	bool is_falling = FALSE;
	
	for(y = 0 ; y < PANEL_FIELD_H ; y++ ){
		for(x = 0 ; x < PANEL_FIELD_W ; x++ ){
			if( play.panel[x][y].type == PANEL_TYPE_NONE ) continue;
			
			switch( play.panel[x][y].state ){
				case PANEL_STATE_STAYING:
					// 落下開始
					if( y < PANEL_FIELD_H-1 && (play.panel[x][y+1].type == PANEL_TYPE_NONE || play.panel[x][y+1].state == PANEL_STATE_FALLING )){
						
						play.panel[x][y].state = PANEL_STATE_FALLING;
						play.panel[x][y].fall_diff = 0;
						play.panel[x][y].timer = 0;
						play.panel[x][y].is_player_droped = FALSE;
						
						is_falling = TRUE;
					}
					break;
				case PANEL_STATE_FALLING:
					// 落下中
					if( play.panel[x][y].is_player_droped ){
						play.panel[x][y].fall_diff += PANEL_FALL_SPEED_PLAYER;
					}else{
						play.panel[x][y].fall_diff += PANEL_FALL_SPEED_NORMAL;
					}
					
					if( play.panel[x][y].fall_diff >= 1.0f ){
						// 続けて落下
						memcpy( &play.panel[x][y+1] , &play.panel[x][y] , sizeof(Panel) );
						play.panel[x][y].type = PANEL_TYPE_NONE;
						play.panel[x][y+1].fall_diff -= 1.0f;
					}
					if( ! play.panel[x][y].is_player_droped ) is_falling = TRUE;
					
					// 着地
					if( y == PANEL_FIELD_H-1 || play.panel[x][y+1].type != PANEL_TYPE_NONE ){
						play.panel[x][y].state = PANEL_STATE_STAYING;
						play.panel[x][y].fall_diff = 0;
						play.panel[x][y].timer = 0;
						play.panel[x][y].is_player_droped = FALSE;
					}
					
					break;
				default:
					break;
			}
		}
	}
	return is_falling;
}

bool check_panel_will_erase(){
	int x,y;
	bool will_erase;		
	
	for(y = 0 ; y < PANEL_FIELD_H ; y++ ){
		for(x = 0 ; x < PANEL_FIELD_W ; x++ ){
			if( play.panel[x][y].type == PANEL_TYPE_NONE ) continue;
			
			switch( play.panel[x][y].state ){
				case PANEL_STATE_STAYING:
					// 2x2
					if( x < PANEL_FIELD_W-1 && y < PANEL_FIELD_H-1
					   && play.panel[x][y].type == play.panel[x][y+1].type && play.panel[x][y].type == play.panel[x+1][y+1].type
					   && play.panel[x][y].type == play.panel[x+1][y].type 
					   && (play.panel[x][y+1].state == PANEL_STATE_STAYING)
					   && (play.panel[x+1][y].state == PANEL_STATE_STAYING)
					   && (play.panel[x+1][y+1].state == PANEL_STATE_STAYING) )
					{
						play.panel[x][y].state = PANEL_STATE_ERASING;
						play.panel[x][y].timer = 0;
						play.panel[x][y+1].state = PANEL_STATE_ERASING;
						play.panel[x][y+1].timer = 0;
						play.panel[x+1][y].state = PANEL_STATE_ERASING;
						play.panel[x+1][y].timer = 0;
						play.panel[x+1][y+1].state = PANEL_STATE_ERASING;
						play.panel[x+1][y+1].timer = 0;
						will_erase = true;
					}
					break;
				default:
					break;
			}	
		}
	}
	return will_erase;
}

bool check_erase_panel(){
	int x,y;
	bool is_erasing;
	
	int tmp_chain = chain_counter;
	
	for(y = 0 ; y < PANEL_FIELD_H ; y++ ){
		for(x = 0 ; x < PANEL_FIELD_W ; x++ ){
			if( play.panel[x][y].type == PANEL_TYPE_NONE ) continue;
			
			switch( play.panel[x][y].state ){
				//case PANEL_STATE_PRE_ERASING:
					play.panel[x][y].state = PANEL_STATE_ERASING;
					play.panel[x][y].timer = 0;
					
					tmp_chain++;
					if( tmp_chain < 10 ){
						add_effect_pertic_with_field( x , y , 1 );
					}else{
						add_effect_pertic_with_field( x , y , 4 );
					}
					break;
				default:
					break;
			}	
		}
	}
	return is_erasing;
}

void erase_ignited_panel(){
	int x,y;
	play.score += get_gain_score_for_view( chain_counter , continuing_chain_counter , continuing_counter );
	for(y = 0 ; y < PANEL_FIELD_H ; y++ ){
		for(x = 0 ; x < PANEL_FIELD_W ; x++ ){
			if( play.panel[x][y].state == PANEL_STATE_ERASING && play.panel[x][y].type != PANEL_TYPE_NONE ){
				GameState_addEffect(x * PANEL_SIZE + PANEL_FIELD_OFFSET_X , (PANEL_FIELD_H - y + play.raised_field) * PANEL_SIZE + PANEL_FIELD_OFFSET_Y,
						   (rand() % 127 - 63)*0.1 , (rand() % 127 - 31) * 0.1 ,EFFECT_TYPE_FLYBLOCK, play.panel[x][y].type);
				play.panel[x][y].type = PANEL_TYPE_NONE;
				
				play.cleared_panel_counter++;
			}
		}
	}
}

int count_erasing_now(){
	int x,y,cnt = 0;
	for(y = 0 ; y < PANEL_FIELD_H ; y++ ){
		for(x = 0 ; x < PANEL_FIELD_W ; x++ ){
			if( play.panel[x][y].state == PANEL_STATE_ERASING && play.panel[x][y].type != PANEL_TYPE_NONE ) cnt++;
		}
	}
	return cnt;
}

int get_field_count(){
	int x,y,cnt = 0;
	for( y = 0 ; y < PANEL_FIELD_H ; y++ ){
		for( x = 0 ; x < PANEL_FIELD_W ; x++ ){
			if( play.panel[x][y].type != PANEL_TYPE_NONE ) cnt++;
		}
	}
	return cnt;
}
int get_field_height(){
	int x,y;
	for( y = 0 ; y < PANEL_FIELD_H ; y++ ){
		for( x = 0 ; x < PANEL_FIELD_W ; x++ ){
			if( play.panel[x][y].type != PANEL_TYPE_NONE ) return y;
		}
	}
	return y;
}
int get_field_lowest(){
	int x,y;
	for( y = 0 ; y < PANEL_FIELD_H ; y++ ){
		bool f = true;
		for( x = 0 ; x < PANEL_FIELD_W ; x++ ){
			if( play.panel[x][y].type == PANEL_TYPE_NONE ) f = false;
		}
		if( f ) break;
	}
	return y-1;
}
int get_gain_score_for_view( int chain_counter , int continuing_counter , int continuing_chain_counter ){
	//					  0   +10   +10   +10   +10   +20   +30    +40   +50   +60   +80   +90  +100
	int score_table[] = {   0 ,  10 ,  20 ,  30 ,  40 ,  60 ,  90 ,  130 , 180 , 240 , 320 , 410 , 510 };
	int base_score = 0;
	if( chain_counter < sizeof(score_table) / sizeof(int) ){
		base_score = score_table[ chain_counter ];
	}else{
		int last_idx = sizeof(score_table) / sizeof(int) - 1;
		base_score = score_table[ last_idx ] + (chain_counter - last_idx) * 100;
	}
	
	return base_score;
}

//-----

bool GameState_saveState(){
	Hell_saveBytes("ResumeGame", &play , sizeof(PlayState) );
	return true;
}
bool GameState_resumeState(){
	bool result = Hell_loadBytes("ResumeGame", &play, sizeof(PlayState));
	if( result ){
		NSLog( @"resume ok" );
	}else{
		NSLog( @"RESUME FAIL");
	}
	return true;
}

//-----


void GameState_draw(){
	GameStateGraphics_draw();
}

//-----

void GameState_callbackTouchBegan( Hell_TouchEvent* touchevents , int eventCount ){
	if( eventCount > 2 ) init_field();
	
	int w = get_next_line_width(0);
	next_line_drop_x = ((touchevents[0].x - PANEL_FIELD_OFFSET_X)/PANEL_SIZE - get_next_line_width(0)/2.0);
	if( next_line_drop_x < 0 ) next_line_drop_x = 0;
	if( next_line_drop_x + w > PANEL_FIELD_W ) next_line_drop_x = PANEL_FIELD_W - w;
	slide_dist = 0;
	grab = TRUE;
}

void GameState_callbackTouchMoved( Hell_TouchEvent* touchevents , int eventCount ){	
	int w = get_next_line_width(0);
	
	slide_dist += touchevents[0].x - touchevents[0].prev_x;
	if(slide_dist > PANEL_SIZE ){
		slide_dist = 0;
		next_line_drop_x++;
	}
	if(slide_dist < -PANEL_SIZE ){
		slide_dist = 0;
		next_line_drop_x--;
	}
	if( next_line_drop_x < 0 ) next_line_drop_x = 0;
	if( next_line_drop_x + w > PANEL_FIELD_W ) next_line_drop_x = PANEL_FIELD_W - w;
	
	grab = TRUE;
}

void GameState_callbackTouchEnded( Hell_TouchEvent *touchevents , int eventCount ){
	int x;
	for(x = 0; x < PANEL_FIELD_W ; x++){
		if( x + next_line_drop_x >= PANEL_FIELD_W || play.next_line[x][0] == PANEL_TYPE_NONE
		   || play.panel[x + next_line_drop_x][0].type != PANEL_TYPE_NONE) continue;
		
		play.panel[x + next_line_drop_x][0].type = play.next_line[x][0];
		play.panel[x + next_line_drop_x][0].state = PANEL_STATE_FALLING;
		play.panel[x + next_line_drop_x][0].timer = 0;
		play.panel[x + next_line_drop_x][0].fall_diff = 0;
		play.panel[x + next_line_drop_x][0].is_player_droped = TRUE;
	}
	set_next_line();
	grab = FALSE;
	
	Hell_playSoundEffect( HELL_SOUND_DROP );
}
